package net.asfun.jvalog.domain;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.jdo.Query;

import com.google.appengine.api.datastore.Text;

import net.asfun.jvalog.except.InteractException;
import net.asfun.jvalog.misc.Formater;
import net.asfun.jvalog.misc.HtmlUtil;
import net.asfun.jvalog.po.Label;
import net.asfun.jvalog.po.Paper;
import net.asfun.jvalog.po.Phase;
import net.asfun.jvalog.po.Relation;
import net.asfun.jvalog.po.Sort;
import net.asfun.jvalog.service.Jdoer;
import net.asfun.jvalog.service.JdoerFactory;
import net.asfun.jvalog.task.UpdateManager;
import net.asfun.jvalog.view.PagerInfo;
import net.asfun.jvalog.view.SiteInfo;

public class Post {
	
	protected Paper paper;
	
	private List<Relation> categories;
	
	private List<Relation> tags;
	
	private List<Relation> comments;
	
	public Post() {
		paper = new Paper();
		paper.setType(0);
	}
	
	private Post(Paper p) {
		if ( null == p ) {
			throw new InteractException("Post not found");
		}
		paper = p;
	}

	public static List<Post> list(PagerInfo pager) {
		Jdoer<Paper> dbp = JdoerFactory.getPaperJdo();
		List<Post> list = new ArrayList<Post>();
		pager.setTotal(dbp.all().size());
		Query q = dbp.prepareQuery();
		q.setFilter("type == 0");
		q.setOrdering("published ascending, date descending");
		q.setRange(pager.getStart(), pager.getEnd());
		Collection<Paper> res = dbp.more(q);
		for(Paper ap : res ) {
			list.add(new Post(ap));
		}
		return list;
	}
	
	public static List<Post> listPublished(PagerInfo pager) {
		Jdoer<Paper> dbp = JdoerFactory.getPaperJdo();
		List<Post> list = new ArrayList<Post>();
		Query q = dbp.prepareQuery();
		q.setFilter("type == 0 && published == true");
		pager.setTotal(dbp.more(q).size());
		q.setOrdering("date descending");
		q.setRange(pager.getStart(), pager.getEnd());
		Collection<Paper> res = dbp.more(q);
		for(Paper ap : res ) {
			Post post = new Post(ap);
			list.add(post);
		}
		if ( list.isEmpty() && pager.getIndex() > 1 ) {
			throw new InteractException("Post pager no record");
		}
		return list;
	}
	
	public List<Comment> getComments() {
		setComments();
		List<Comment> list = new ArrayList<Comment>();
		for (Relation rel : comments ) {
			Comment comm = Comment.load(rel.getRight().getId());
			if ( comm.isPublished() )
				list.add(comm);
		}
		return list;
	}
	
	private void setComments() {
		if ( null == comments ) {
			comments = Bridge.getRelationsByLeft(paper.getKey(), Bridge.POST_COMMENT);
		}
	}
	
	public int getCommentCount() {
		return Counter.getCount(paper.getKey());
	}

	public void setTitle(String title) {
		paper.setTitle(title);
	}
	
	public String getTitle() {
		return paper.getTitle();
	}

	public void setSlogan(String slogan) {
		paper.setSlogan(slogan);
	}
	
	public String getSlogan() {
		return paper.getSlogan();
	}

	public void setExcerpt(String excerpt) {
		paper.setExcerpt(excerpt);
	}
	
	public String getExcerpt() {
		return paper.getExcerpt();
	}

	public void setSource(String source) {
		paper.setSource(source);
	}
	
	public String getSource() {
		return paper.getSource();
	}
	
	public Long getId() {
		return paper.getKey().getId();
	}
	
	public String getLink() {
		return SiteInfo.getInstance().getUrl() + paper.getUri() + ".html";
	}
	
	private void generateURI() {
		StringBuffer buff = new StringBuffer();
		buff.append(Formater.date2string(paper.getDate(),"yyyy/MM/dd/"))
			.append(paper.getSlogan());
		paper.setUri(buff.toString());
	}
	
	public String getPreview() {
		String content = paper.getContent().getValue();
		if ( content.length() > 400 ) {
			return HtmlUtil.preview(content, 400) + "<a href='"
			+ getLink() + "'>More...</a>";
		} else {
			return content;
		}
	}
	
	public List<Tag> getTags() {
		List<Tag> list = new ArrayList<Tag>();
		setTags();
		for(Relation rel : tags) {
			list.add(Tag.load(rel.getRight().getId()));
		}
		return list;
	}

	public void setTagNames(String tagnames) {
		tags = new ArrayList<Relation>();
		String[] tagns = tagnames.split(",");
		for(String tag : tagns) {
			Label label = Tag.locate(tag);
			Relation rel = new Relation();
			rel.setRight(label.getKey());
			rel.setType(Bridge.POST_TAG);
			tags.add(rel);
		}
	}
	
	public String getTagNames() {
		setTags();
		StringBuffer buff = new StringBuffer();
		Jdoer<Label> dbl = JdoerFactory.getLabelJdo();
		for (Relation rel : tags) {
			Label label = dbl.load(rel.getRight());
			if ( label != null ) {
				buff.append(label.getName()).append(",");
			}
		}
		if (buff.length() > 0) {
			buff.deleteCharAt(buff.length() - 1);
		}
		return buff.toString();
	}
	
	private void setTags() {
		if ( null == tags ) {
			tags = Bridge.getRelationsByLeft(paper.getKey(), Bridge.POST_TAG);
		}
	}

	public void setCategories(Long[] categoryIds) {
		categories = new ArrayList<Relation>();
		for(Long id : categoryIds) {
			Sort sort = Category.locate(id);
			Relation rel = new Relation();
			rel.setRight(sort.getKey());
			rel.setType("post_category");
			categories.add(rel);
		}
	}
	
	private void setCategories() {
		if ( null == categories) {
			categories = Bridge.getRelationsByLeft(paper.getKey(), Bridge.POST_CATEGORY);
		}
	}
	
	public List<Category> getCategories() {
		List<Category> list = new ArrayList<Category>();
		setCategories();
		for(Relation rel : categories) {
			list.add(Category.load(rel.getRight().getId()));
		}
		return list;
	}
	
	public List<Long> getCategoryIds() {
		setCategories();
		List<Long> cids = new ArrayList<Long>();
		for(Relation rel : categories) {
			cids.add(rel.getRight().getId());
		}
		return cids;
	}

	public void save() {
		generateURI();
		//make sure uri unique
		if ( ! Unifier.isUnique("Paper", "uri", paper.getUri()) ) {
			paper.setSlogan(paper.getSlogan() + "~1");
			this.save();
			return;
		}
		Jdoer<Paper> dbp = JdoerFactory.getPaperJdo();
		dbp.save(paper);
		Unifier.setUnique(paper.getKey(), "uri", paper.getUri());
		for (Relation tag : tags) {
			tag.setLeft(paper.getKey());
			dbp.save(tag);
		}
		for (Relation cat : categories) {
			cat.setLeft(paper.getKey());
			dbp.save(cat);
		}
		if ( paper.isPublished() ) {
			UpdateManager.enableCounterTask();
		}
	}

	public static Post load(Long id) {
		Jdoer<Paper> dbp = JdoerFactory.getPaperJdo();
		return new Post(dbp.load(id));
	}
	
	public static Post load(String uri) {
		Jdoer<Paper> dbp = JdoerFactory.getPaperJdo();
		Query q = dbp.prepareQuery();
		q.setFilter("uri == link && published == true");
		q.declareParameters("String link");
		return new Post(dbp.unique(q, uri));
	}

	public void setArticle(String article) {
		paper.setContent(new Text(article));
	}
	
	public String getArticle() {
		return paper.getContent().getValue();
	}

	public void setDate(Date date) {
		if ( paper.getDate() != null ) {
			if ( ! Formater.date2string(date, "yyyyMM").equals(
					Formater.date2string(paper.getDate(), "yyyyMM"))) {
				Phase pha = Archive.getStage(paper.getDate());
				pha.setCount(pha.getCount()-1);
				Phase ph2 = Archive.getStage(date);
				ph2.setCount(ph2.getCount()+1);
			}
		} else {
			Phase pha = Archive.getStage(date);
			pha.setCount(pha.getCount()+1);
		}
		paper.setDate(date);
	}
	
	public Date getDate() {
		return paper.getDate();
	}

	public void update() {
		generateURI();
		//make sure uri unique
		if ( ! Unifier.isUnique(paper.getKey(), "uri", paper.getUri()) ) {
			paper.setSlogan(paper.getSlogan() + "~1");
			this.update();
			return;
		}
		Unifier.setUnique(paper.getKey(), "uri", paper.getUri());
		//del old relation
		Jdoer<Relation> dbr = JdoerFactory.getRelationJdo();
		dbr.delete(Bridge.getRelationsByLeft(paper.getKey(), Bridge.POST_TAG));
		dbr.delete(Bridge.getRelationsByLeft(paper.getKey(), Bridge.POST_CATEGORY));
		//add new relation
		for (Relation tag : tags) {
			tag.setLeft(paper.getKey());
			dbr.save(tag);
		}
		for (Relation cat : categories) {
			cat.setLeft(paper.getKey());
			dbr.save(cat);
		}
		if ( paper.isPublished() ) {
			UpdateManager.enableCounterTask();
		}
	}

	public void setPublished(boolean b) {
		paper.setPublished(b);
	}
	
	public boolean isPublished() {
		return paper.isPublished();
	}

	public static List<Post> listRecent(int size) {
		Jdoer<Paper> dbp = JdoerFactory.getPaperJdo();
		List<Post> list = new ArrayList<Post>();
		Query q = dbp.prepareQuery();
		q.setFilter("type == 0 && published == true");
		q.setOrdering("date descending");
		q.setRange(0, size);
		Collection<Paper> res = dbp.more(q);
		for (Paper paper : res ) {
			Post post = new Post();
			post.paper = paper;
			list.add(post);
		}
		return list;
	}

	public static void remove(Long id) {
		Jdoer<Paper> dbp = JdoerFactory.getPaperJdo();
		Paper paper = dbp.load(id);
		if ( paper != null ) {
			Phase pha = Archive.getStage(paper.getDate());
			pha.setCount(pha.getCount()-1);
			//DELETE UNIQUE ENTRY
			Unifier.unsetUnique(paper.getKey(), "uri");
			dbp.delete(Bridge.getRelationsByLeft(paper.getKey(), Bridge.POST_CATEGORY));
			dbp.delete(Bridge.getRelationsByLeft(paper.getKey(), Bridge.POST_TAG));
			dbp.delete(Bridge.getRelationsByLeft(paper.getKey(), Bridge.POST_COMMENT));
			if ( paper.isPublished() ) {
				UpdateManager.enableCounterTask();
			}
			dbp.delete(paper);
		}
	}

	public static List<Post> listArchive(String[] ymd, PagerInfo pager) {
		String from=null, to=null;
		int numb=0;
		switch(ymd.length) {
		case 3 :
			numb = Integer.valueOf(ymd[2]);
			from = ymd[0] + "-" + ymd[1] + "-" + numb;
			to = ymd[0] + "-" + ymd[1] + "-" + (numb+1);
			break;
		case 2 :
			numb = Integer.valueOf(ymd[1]);
			from = ymd[0] + "-" + numb + "-01";
			to = ymd[0] + "-" + (numb+1) + "-01";
			break;
		case 1 :
			numb = Integer.valueOf(ymd[0]);
			from = numb + "-01-01";
			to = (numb+1) + "-01-01";
			break;	
		}
		Jdoer<Paper> dbp = JdoerFactory.getPaperJdo();
		List<Post> list = new ArrayList<Post>();
		Query q = dbp.prepareQuery();
		q.setFilter("type == 0 && published == true && date > afrom && date < ato");
		q.declareParameters("java.util.Date afrom, java.util.Date ato");
		Date afrom = Formater.string2date(from, "yyyy-MM-dd");
		Date ato = Formater.string2date(to, "yyyy-MM-dd");
		pager.setTotal(dbp.more(q, afrom, ato).size());
		q.setRange(pager.getStart(), pager.getEnd());
		Collection<Paper> res = dbp.more(q, afrom, ato);
		for (Paper paper : res ) {
			Post post = new Post();
			post.paper = paper;
			list.add(post);
		}
		return list;
	}

}
